#include "stdafx.h"

namespace classifier_test
{

namespace high_order
{

concept_cutter::concept_cutter()
{
}

concept_cutter::concept_cutter(const concept_cutter& Val)
{
}

void concept_cutter::serialize(serializer& Serializer)
{
}

void concept_cutter::initialize_options(command_line_options& Options) const
{
}

void concept_cutter::apply_arguments(const command_line_arguments& Args)
{
}

void concept_cutter::set_base_classifier(classifier* Val)
{
	BaseClassifier = Val->clone();
}

void concept_cutter::set_base_validation_dataset(validation_dataset* VDataset)
{
	BaseVDataset = VDataset->clone();
}

void concept_cutter::set_data(subarray<const record* const> SetData)
{
	Data = SetData;
}

double concept_cutter::compute_validation_error() const
{
	double Errors = 0;
	int Count = 0;
	for (int I = 0; I < get_chunk_count(); I++)
		Errors += get_chunk(I).verror * get_chunk(I).vdataset->size(),
		Count += get_chunk(I).vdataset->size();
	return Errors / Count;
}

high_order_concept_cutter::high_order_concept_cutter()
{
	BlockSize = 20;
}

high_order_concept_cutter::high_order_concept_cutter(const high_order_concept_cutter& Val)
	: concept_cutter(Val)
{
	BlockSize = Val.BlockSize;
}

smart_ptr<concept_cutter> high_order_concept_cutter::clone() const
{
	return make_smart_ptr(new high_order_concept_cutter(*this));
}

void high_order_concept_cutter::serialize(serializer& Serializer)
{
	concept_cutter::serialize(Serializer);
	Serializer("BlockSize", BlockSize);
}

void high_order_concept_cutter::initialize_options(command_line_options& Options) const
{
	concept_cutter::initialize_options(Options);
	Options.add("block").set_argument("<count>").set_info("# of records in a block, default 20");
}

void high_order_concept_cutter::apply_arguments(const command_line_arguments& Args)
{
	concept_cutter::apply_arguments(Args);
	BlockSize = to_int(Args["block"].get_or_default("20"));
}

void high_order_concept_cutter::compute()
{
	logger::task LTask = clogger()->fine("Cutting data stream into concept occurrences");
	high_order_concept_clustering P;
	P.set_base_classifier(&*BaseClassifier);
	P.set_mode(high_order_concept_clustering::chain_mode);
	int BlockCount = Data.size() / BlockSize;
	P.set_chunk_count(BlockCount);
	for (int I = 0; I < BlockCount; I++)
	{
		smart_ptr<validation_dataset> VDataset = BaseVDataset->clone();
		VDataset->build(make_subarray(Data, BlockSize * I, BlockSize));
		smart_ptr<validation_classifier> VClassifier = VDataset->train(&*BaseClassifier);
		double VError = VClassifier->test_error(&*VDataset);
		P.set_chunk(I, VDataset, VClassifier, VError);
	}
	P.compute();
	Chunks.reserve(P.get_concept_count());
	for (int I = 0; I < BlockCount;)
	{
		int J = I + 1;
		int Concept = P.get_chunk(I).concept;
		while (J < BlockCount && P.get_chunk(J).concept == Concept) J++;
		// if (J - I >= 2)
		{
			Chunks.push_back(chunk());
			chunk& Chunk = Chunks.back();
			Chunk.begin = I * BlockSize;
			Chunk.end = J * BlockSize;
			Chunk.vdataset = P.get_concept(Concept).vdataset;
			Chunk.vclassifier = P.get_concept(Concept).vclassifier;
			Chunk.verror = P.get_concept(Concept).verror;
		}
		I = J;
	}
	clogger()->fine("# of chunks: " + to_string(Chunks.size()));
}

int high_order_concept_cutter::get_chunk_count() const
{
	return Chunks.size();
}

const concept_cutter::chunk& high_order_concept_cutter::get_chunk(int I) const
{
	return Chunks[I];
}

double high_order_concept_cutter::compute_estimated_validation_error() const
{
	return compute_validation_error();
}

}

}
